home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 07 - 1991 / 07.07 Jul 91 / Multi DA Source / Multi DA.p next >
Encoding:
Text File  |  1990-04-12  |  20.5 KB  |  785 lines  |  [TEXT/PJMM]

  1. unit MenuDA;
  2.  
  3. interface
  4.  
  5. function Main (DCtlE: DCtlPtr;
  6.                             IOPB: ParmBlkPtr;
  7.                             driveCall: Integer): OSErr;
  8.  
  9. implementation
  10.  
  11. const
  12. {Define all of the possible driver calls}
  13.     DriverOpen = 0;
  14.     DriverPrime = 1;
  15.     DriverControl = 2;
  16.     DriverStatus = 3;
  17.     DriverClose = 4;
  18.  
  19.     OpenErr = -23;
  20.     CloseErr = -24;
  21.  
  22. {Offsets to resource IDs for main dialog and }
  23. {alert}
  24.     DlogID = 0;
  25.     AboutID = 0;
  26.  
  27. {Subitem resource numbers of our menus}
  28.     AppleMenu = 0;
  29.     AboutItem = 1;
  30.  
  31.     FileMenu = 1;
  32.     NewWindowItem = 1;
  33.     CloseItem = 2;
  34.     QuitItem = 4;
  35.  
  36.     EditMenu = 2;
  37.     undoItem = 1;
  38.     cutItem = 3;
  39.     copyItem = 4;
  40.     pasteItem = 5;
  41.     clearItem = 6;
  42.  
  43.     WindowMenu = 3;
  44.     CleanupItem = 1;
  45.     BeepItem = 2;
  46.  
  47. {Dialog itemlist}
  48.     NewWindowButton = 1;
  49.     EraseButton = 2;
  50.     EditText = 3;
  51.  
  52. type
  53.     MenuBar = (DAMenus, AppMenus);
  54.  
  55. var
  56.     done: boolean;
  57.     SavedMenuList: handle;
  58.     MenuIDs: array[0..WindowMenu] of integer;
  59.     OurMenus: array[0..WindowMenu] of MenuHandle;
  60.     OurMenuBar: boolean;
  61.  
  62.     DCE: DCtlPtr;
  63.     OurName: str255;
  64.     NumWindows: integer;
  65.     WindowCounter: integer;
  66.  
  67. { ********************************************** }
  68. { ************Global Utility Functions********** }
  69. { ********************************************** }
  70. {The actual resource IDs of our resources depends }
  71. {on our desk accessory’s run-time device control }
  72. {reference number (this gets switched around by }
  73. {Font/DA Mover and Suitcase.  The “owned resource” }
  74. {sub-ID never changes, however.  Use our dCtlRefNum }
  75. {to get the run-time ID of our resources}
  76. function GetResID (SubID: integer): integer;
  77.     begin
  78.         GetResID := BOR($C000, SubID + (BSL((Abs(DCE^.dCtlRefNum) - 1), 5)));
  79.     end;
  80.  
  81. {-----------------------DAMenuKey---------------}
  82. {MenuKey does not work from within a desk }
  83. {accessory because the system does not respond }
  84. {correctly to the meta-keys used in DA menus. This }
  85. {procedure is a substitute for MenuKey that }
  86. {performs the function correctly. Given a }
  87. {character, this function will determine if it is a }
  88. {menu meta-character and returns the menu ID in the }
  89. {high order word and the item in the low order word }
  90. {just as MenuKey does.  Note that this code makes }
  91. {use of information stored in our globals, and is }
  92. {NOT directly transferable to other desk }
  93. {accessories.}
  94. function DAMenuKey (cmd: char): longint;
  95.     var
  96.         i, item: integer;
  97.         key: char;
  98.     begin
  99. {Return a ‘0’ as default}
  100.         DAMenuKey := 0;
  101. {Capitalize lowercase letters}
  102.         if (cmd >= 'a') & (cmd <= 'z') then
  103.             cmd := Chr(Ord(cmd) - (Ord('a') - Ord('A')));
  104.  
  105. {Loop through each menu, looking for matches}
  106.         for i := 0 to WindowMenu do
  107.  
  108. {If we find an enabled menu then examine each }
  109. {enabled item in turn until we find a matching }
  110. {command key.}
  111.             if BTST(OurMenus[i]^^.enableFlags, 0) then
  112.                 for item := 1 to CountMItems(OurMenus[i]) do
  113.  
  114.                     if BTST(OurMenus[i]^^.enableFlags, item) then begin
  115.                             GetItemCmd(OurMenus[i], item, key);
  116.                             if key <> Cmd then
  117.                                 cycle
  118.                             else
  119.                                 DAMenuKey := BOR(item, BSL(MenuIDs[i], 16));
  120.                             HiliteMenu(MenuIDs[i]);
  121.                             Exit(DAMenuKey);
  122.                         end
  123.  
  124.     end; {of FUNCTION MyMenuKey}
  125.  
  126.  
  127. {**********************************************}
  128. {************ Menu Handling Routines **********}
  129. {**********************************************}
  130.  
  131. {-------------------InitMenus--------------}
  132. {Fetch and install our menus, remembering the }
  133. {application’s menu bar for later switching. We }
  134. {return the menu ID of any of our menus for }
  135. {installation into the dCtlMenu field of the device }
  136. {control entry. It doesn’t matter exactly which of }
  137. {our menus we return.}
  138. function InitMenus: integer;
  139.     const
  140.         CurApName = $910; {Low memory global}
  141. {Hard code the name of the desk accessory layer in }
  142. {multifinder.  Note that there is a non-breaking }
  143. {space between DA and Handler.}
  144.         DALayer = 'DA Handler';
  145.     var
  146.         i: integer;
  147.         name: str255;
  148.     begin
  149.         for i := 0 to WindowMenu do begin
  150.                 MenuIDs[i] := GetResID(i);
  151.                 OurMenus[i] := GetMenu(MenuIDs[i]);
  152.                 OurMenus[i]^^.MenuID := MenuIDs[i];
  153.             end;
  154.  
  155.         AddResMenu(OurMenus[AppleMenu], 'DRVR');
  156.         InitMenus := MenuIDs[AppleMenu];
  157.  
  158. {If we’ve been loaded into DA Handler, then we dim }
  159. {out our desk accessories. This is done because DA }
  160. {Handler does not like desk accessories opening }
  161. {other desk accessories!}
  162.         if StringPtr(CurApName)^ = DALayer then
  163.             for i := 1 to CountMItems(OurMenus[AppleMenu]) do begin
  164.                     GetItem(OurMenus[AppleMenu], i, name);
  165.                     if name[1] = char($00) then
  166.                         DisableItem(OurMenus[AppleMenu], i);
  167.                 end;
  168.     end;
  169.  
  170.  
  171. {-----------------SaveMenuBar-------------------}
  172. {This procedure is called in order to make a copy }
  173. {of, and save, the current menu bar data structure. }
  174. {It is conceivable that it may be called twice in a }
  175. {row, so dispose of any previously saved menubars.}
  176. procedure SaveMenuBar;
  177.     begin
  178.         if SavedMenuList <> nil then
  179.             Disposhandle(SavedMenuList);
  180.         SavedMenuList := GetMenuBar;
  181.     end;
  182.  
  183. {------------------InsertDAMenus-----------------}
  184. procedure InsertDAMenus;
  185.     var
  186.         i: integer;
  187.     begin
  188.         ClearMenuBar;
  189.         for i := 0 to WindowMenu do
  190.             InsertMenu(OurMenus[i], 0);
  191.     end;
  192.  
  193. {------------------AdjustMenus-------------------}
  194. {This procedure adjusts the menus periodically to }
  195. {allow for changes It would typically be used }
  196. {enabling and disabling menuitems, changing item }
  197. {names, etc. as appropriate for the DA’s state.}
  198. procedure AdjustMenus;
  199.     begin
  200.         if NumWindows > 1 then
  201.             enableItem(OurMenus[Filemenu], CloseItem)
  202.         else
  203.             disableItem(OurMenus[FileMenu], CloseItem)
  204.     end;
  205.  
  206.  
  207. {--------------------SetMenu--------------------}
  208. {This procedure installs our menubar when one of }
  209. {our windows becomes active. Pass it “DAMenus” to }
  210. {install our DA’s menubar. Pass it “AppMenus” to }
  211. {restore the application’s menubar.}
  212. procedure SetMenu (which: MenuBar);
  213.     var
  214.         mBarEnable: ^integer;
  215.  
  216. {-----}
  217. {FUNCTION OursIsActivating is used to determine if }
  218. {one of our windows is about to come to the top. If }
  219. {this is going to happen, then there is no use }
  220. {switching menus when one of our windows is }
  221. {deactivated. It calls EventAvail, looking for an }
  222. {activate event in one of our own windows.}
  223.     function OursIsActivating: boolean;
  224.         var
  225.             Evt: eventRecord;
  226.             kind: integer;
  227.         begin
  228.             OursIsActivating := false;
  229.             if EventAvail(activMask, Evt) & (BitAnd(Evt.modifiers, activeFlag) <> 0) then begin
  230.                     kind := WindowPeek(Evt.message)^.windowkind;
  231.                     OursIsActivating := (kind = DCE^.dctlRefNum)
  232.                 end
  233.         end;
  234.  
  235. {-----}
  236.     begin {PROCEDURE SetMenu}
  237.         mBarEnable := Pointer($A20);
  238.   {Install DA's menubar if requested to and our }
  239. {menu bar isn't there already.}
  240.         if (which = DAMenus) then begin
  241.                 if not OurMenuBar then begin
  242.                         SaveMenuBar;
  243.                         InsertDAMenus;
  244.                         MBarEnable^ := DCE^.dctlMenu;
  245.                         OurMenuBar := true;
  246.                         DrawMenuBar
  247.                     end
  248.             end
  249.         else if not OursIsActivating then begin
  250.                 SetMenuBar(SavedMenuList);
  251.                 MBarEnable^ := 0;
  252.                 OurMenuBar := False;
  253.                 DrawMenuBar
  254.             end
  255.     end;
  256.  
  257. {**********************************************}
  258. { ********* Window Handling Routines ********* }
  259. {********************************************* }
  260.  
  261. {----------------ModifyWindows---------------- }
  262. {PROCEDURE ModifyWindows is passed a procedure }
  263. {parameter. It loops through our windows performing }
  264. {the passed procedure on each of our windows, }
  265. {starting with the window passed in Start. By }
  266. {calling itself recursively, it performs the action }
  267. {on the bottom-most window first and the top-most }
  268. {window last. A neat trick! }
  269. procedure ModifyWindows (Start: univ WindowPeek;
  270.                                 procedure DoSomething (aW: Windowptr));
  271.     begin
  272.         if Start = nil then
  273.             Exit(ModifyWindows);
  274.         WindowCounter := 1;
  275.         ModifyWindows(Start^.NextWindow, DoSomething);
  276.         if Start^.windowKind = DCE^.dctlRefNum then begin
  277.                 DoSomething(WindowPtr(Start));
  278.                 WindowCounter := succ(WindowCounter)
  279.             end;
  280.     end;
  281.  
  282. {------------------------------}
  283. {The following procedures are used in calls to the }
  284. {ModifyWindows procedure to do the same task to }
  285. {each of our windows in turn...}
  286.  
  287. {PROCEDURE CleanupProc stacks the windows in place }
  288. {one by one, using the WindowCounter global to keep }
  289. {track of which window we’re working on.}
  290. procedure CleanupProc (theWind: windowptr);
  291.     const
  292.         spacing = 10;
  293.         vStart = 40;
  294.         hStart = 5;
  295.     begin
  296.         HideWindow(theWind);
  297.         MoveWindow(theWind, WindowCounter * spacing + hStart, WindowCounter * spacing + vStart, true);
  298.         ShowWindow(theWind)
  299.     end;
  300.  
  301. {------------------------------}
  302. procedure BringWindowForward (aW: Windowptr);
  303.     begin
  304.         BringToFront(aW)
  305.     end;
  306.  
  307. {--------------------OpenAWindow-----------------}
  308. {FUNCTION OpenAWindow opens up a new window, }
  309. {stores our DA’s dCtlRefNum into its windowKind }
  310. {field so that the system knows it’s a DA’s window, }
  311. {and sets the title and initial location in an }
  312. {appropriate manner. We use the run-time name of }
  313. {our desk accessory to form the window title. We }
  314. {also bump up our window counter to keep track of }
  315. {how many windows are open. This would be a good }
  316. {place to store the windowptr into an array or }
  317. {linked list, in order to keep track of the windows }
  318. {more carefully.}
  319. function OpenAWindow: Windowptr;
  320.     var
  321.         aW: windowptr;
  322.         WindowNo: str255;
  323.         theID: integer;
  324.     begin
  325.         theID := GetResID(DlogID);
  326.         aW := GetNewDialog(theID, nil, pointer(-1));
  327.         if aW <> nil then begin
  328.                 windowpeek(aW)^.windowKind := DCE^.dCtlRefNum;
  329.                 NumToString(NumWindows, WindowNo);
  330.                 SetWTitle(aW, concat(OurName, ' ', WindowNo));
  331.                 WindowCounter := NumWindows;
  332.                 CleanUpProc(aW);
  333.                 NumWindows := NumWindows + 1;
  334.             end;
  335.         OpenAWindow := aW
  336.     end;
  337.  
  338.  
  339. {-----------------CloseAWindow------------------}
  340. {PROCEDURE CloseAWindow closes down one of our }
  341. {windows and decrements the NumWindows counter, in }
  342. {a more sophisticated desk accessory, it would }
  343. {handle disposing of the various data structures, }
  344. {files, etc. associated with the window. Note that }
  345. {if the window we're closing is the same as that }
  346. {stored in the dCtlWindow field, we must update the }
  347. {field to point to a current valid window. Local }
  348. {procedure UpdateDCE handles this task.}
  349. procedure CloseAWindow (aWindow: WindowPtr);
  350.  
  351. {-----}
  352. {Procedure UpdateDCE sets the DCE^.dCtlWindow }
  353. {field to point to our topmost open window.  The }
  354. {dCtlWindow field should always point to a valid }
  355. {window or the desk accessory will die horribly in }
  356. {a matter of ticks.}
  357.     procedure UpdateDCE;
  358.         var
  359.             aWindow: WindowPeek;
  360.         begin
  361.             DCE^.dCtlWindow := nil;
  362.             aWindow := WindowPeek(FrontWindow);
  363.             while aWindow <> nil do
  364.                 if aWindow^.WindowKind = DCE^.dCtlRefNum then begin
  365.                         DCE^.dCtlWindow := pointer(aWindow);
  366.                         Exit(UpdateDCE);
  367.                     end
  368.                 else
  369.                     aWindow := aWindow^.NextWindow;
  370.         end; {PROCEDURE UpdateDCE}
  371.  
  372. {----}
  373.     begin {PROCEDURE CloseAWindow}
  374.         DisposDialog(aWindow);
  375.         NumWindows := NumWindows - 1;
  376.  
  377.         if DCE^.dCtlWindow = aWindow then
  378.             UpdateDCE;
  379.         SetMenu(AppMenus);
  380.     end;
  381.  
  382. { ********************************************** }
  383. { ***************** Menu Handlers*************** }
  384. { ********************************************** }
  385.  
  386. {------------------DoApple-----------------------}
  387. {PROCEDURE DoApple handles the apple menu}
  388. procedure DoApple (itemNo: integer);
  389.     var
  390.         dummy: integer;
  391.         name: str255;
  392.     begin
  393.         if itemNo = AboutItem then
  394.             dummy := Alert(GetResId(AboutID), nil)
  395.         else begin
  396.                 GetItem(OurMenus[applemenu], itemNo, name);
  397.                 dummy := OpenDeskAcc(name);
  398.             end
  399.     end;
  400.  
  401. {--------------------DoFile---------------------}
  402. {PROCEDURE DoFile handles the file menu}
  403. procedure DoFile (ItemNo: integer);
  404.     var
  405.         dummy: windowptr;
  406.     begin
  407.         case ItemNo of
  408.             NewWindowItem: 
  409.                 dummy := OpenAWindow;
  410.             CloseItem: 
  411.                 CloseAWindow(FrontWindow);
  412.             QuitItem: 
  413.                 done := true;
  414.         end;
  415.     end;
  416.  
  417. {------------------DoEdit-----------------------}
  418. {PROCEDURE DoEdit handles the Edit Menu}
  419. procedure DoEdit (ItemNo: integer);
  420.     var
  421.         OurDlog: DialogPtr;
  422.         dummy: integer;
  423.     begin
  424.         OurDlog := FrontWindow;
  425.         if OurDlog <> nil then begin
  426.    {Move the public scrap to the TE scrap for the }
  427. {dialog manager’s use.}
  428.                 dummy := TEFromScrap;
  429.                 case ItemNo of
  430.                     undoItem: 
  431.                         sysbeep(5);
  432.                     cutItem: 
  433.                         DlgCut(OurDlog);
  434.                     copyItem: 
  435.                         DlgCopy(OurDlog);
  436.                     pasteItem: 
  437.                         DlgPaste(OurDlog);
  438.                     clearItem: 
  439.                         DlgDelete(OurDlog);
  440.                     otherwise
  441.                 end;
  442.    {Move the TE scrap to the public scrap.}
  443.                 dummy := ZeroScrap;
  444.                 dummy := TEToScrap
  445.             end
  446.         else
  447.             sysbeep(5);
  448.     end;
  449.  
  450. {-----------------DoWindowMenu------------------}
  451. {PROCEDURE DoWindowMenu handles the Windows menu}
  452. procedure DoWindowMenu (itemNo: integer);
  453.     begin
  454.         case itemNo of
  455.             CleanupItem: 
  456.                 modifyWindows(FrontWindow, CleanupProc);
  457.             BeepItem: 
  458.                 sysbeep(20);
  459.         end;
  460.     end;
  461.  
  462. {--------------------DoMenus-----------------}
  463. {PROCEDURE DoMenus is the main dispatch for all }
  464. {menu selections. Since the menu IDs are determined }
  465. {onlyat run time, we cannot use a CASE constant }
  466. {structure here. Instead, we use a series of IF }
  467. {ELSE statements to determine which of our menus }
  468. {was chosen.}
  469. procedure DoMenus (MenuNo, ItemNo: integer);
  470.     var
  471.         Str1, Str2: str255;
  472.         dummy: integer;
  473.         OurDlog: dialogptr;
  474.     begin
  475.         if MenuNo = MenuIDs[AppleMenu] then
  476.             DoApple(ItemNo)
  477.         else if MenuNo = MenuIDs[FileMenu] then
  478.             DoFile(ItemNo)
  479.         else if MenuNo = MenuIDs[EditMenu] then
  480.             DoEdit(ItemNo)
  481.         else if MenuNo = MenuIDs[WindowMenu] then
  482.             DoWindowMenu(ItemNo);
  483.         Hilitemenu(0);
  484.     end;
  485.  
  486.  
  487. {***********************************************}
  488. {*********** Event Handling Routines ***********}
  489. {***********************************************}
  490.  
  491. {-------------------DoActivate------------------}
  492. {PROCEDURE DoActivate handles activate/deactivate }
  493. {events in our DA's windows. On an activate event }
  494. {we install the DA's menu bar and update }
  495. {DCE^.dCtlWindow field to point to the active }
  496. {window. This assures that if our DA is later }
  497. {selected from the application's apple menu, our }
  498. {current active window will be brought to the }
  499. {foreground.}
  500. procedure DoActivate (var Evt: eventrecord);
  501.     var
  502.         active: boolean;
  503.         theWindow: Windowptr;
  504.         kind: integer;
  505.     begin
  506.         active := BitAnd(Evt.modifiers, activeFlag) <> 0;
  507.         if active then begin
  508.                 theWindow := pointer(Evt.message);
  509.                 kind := windowPeek(theWindow)^.windowkind;
  510.                 if kind <> DCE^.dctlRefNum then
  511.                     Exit(DoActivate);
  512.                 DCE^.dCtlWindow := theWindow;
  513.                 SetMenu(DAMenus);
  514.             end
  515.         else
  516.             SetMenu(AppMenus);
  517.     end;
  518.  
  519. {------------------MetaKey------------------}
  520. {FUNCTION MetaKey checks whether the cloverleaf key }
  521. {was pressed during keypress. If so, it gets the }
  522. {menu and item from DAMenuKey and passes the menu }
  523. {selection on to DoMenus. If the cloverleaf was not }
  524. {depressed, then CmdKey returns FALSE so that the }
  525. {caller knows to handle the keypress normally.}
  526. function MetaKey (var Evt: eventrecord): boolean;
  527.     var
  528.         aChr: char;
  529.         Tangled: longint;
  530.     begin
  531.         if BitAnd(cmdKey, Evt.modifiers) <> 0 then begin
  532.                 aChr := Char(BitAnd(Evt.message, charCodeMask));
  533.                 Tangled := DAMenuKey(aChr);
  534.                 DoMenus(HiWord(Tangled), LoWord(Tangled));
  535.                 MetaKey := true
  536.             end
  537.         else
  538.             MetaKey := false;
  539.     end;
  540.  
  541. {--------------------DoIdle----------------------}
  542. {PROCEDURE DoIdle gets called for null events. It }
  543. {does two things:}
  544. {{ 1. It checks the cursor and changes it into an }
  545. {I-beam when the cursor is over the window’s text }
  546. {edit field. }
  547. { 2. It manufacturer’s a “dummy” null event and }
  548. {calls DialogSelect so that the text edit cursor }
  549. {gets flashed.}
  550. {This is the place to do any other background }
  551. {processing.}
  552. procedure DoIdle;
  553.     var
  554.         theDlog: DialogPtr;
  555.         itemHit: integer;
  556.         event: EventRecord;
  557.         dummy: boolean;
  558.         aPt: point;
  559.     begin
  560.         theDlog := DCE^.dctlWindow;
  561.  
  562. {First make the mouse into an I-Beam if we’re }
  563. {above the text field}
  564.         GetMouse(aPt);
  565.         with dialogPeek(theDlog)^ do begin
  566.                 if textH <> nil then
  567.                     if PtInRect(aPt, textH^^.viewRect) then
  568.                         SetCursor(GetCursor(IBeamCursor)^^)
  569.                     else
  570.                         initCursor
  571.             end;
  572.  
  573. {Next call DialogSelect with a null event in order }
  574. {to blink the cursor}
  575.         event.what := NullEvent;
  576.         dummy := dialogSelect(event, theDlog, Itemhit)
  577.     end;
  578.  
  579. {-------------------HandleEvents-----------------}
  580. {PROCEDURE HandleEvents is the main dispatcher for }
  581. {all events appertaining to our DA.}
  582. procedure HandleEvents (var Evt: EventRecord);
  583.     var
  584.         dummy: windowptr;
  585.         itemHit: integer;
  586.         WhichDialog: DialogPtr;
  587.     begin
  588.         AdjustMenus;
  589.  
  590. {We do some pre-processing before calling }
  591. {DialogSelect}
  592.         case Evt.what of
  593.             ActivateEvt: 
  594.                 DoActivate(Evt);
  595.             KeyDown, AutoKey: 
  596.                 if MetaKey(Evt) then
  597.                     Exit(HandleEvents);
  598.             otherwise
  599.         end;
  600.  
  601. {Here we call DialogSelect to do most of the }
  602. {housekeeping window tasks}
  603.         if DialogSelect(Evt, whichDialog, Itemhit) then
  604.             case itemHit of
  605.                 NewWindowButton: 
  606.                     dummy := OpenAWindow;
  607.                 EraseButton: 
  608.                     begin
  609.                         SelIText(whichDialog, EditText, 0, 10000);
  610.                         DlgDelete(WhichDialog);
  611.                     end;
  612.                 otherwise
  613.             end;
  614.     end; {procedure HandleEvents}
  615.  
  616.  
  617. {************************************************}
  618. {**************** Main DA Routines **************}
  619. {************************************************}
  620.  
  621. { ================== CLOSE ======================}
  622. function CLOSE: OSErr;
  623.     var
  624.         i: integer;
  625.     begin
  626.         Close := NoErr;
  627.  
  628. {If we don’t have any window, then we haven’t been }
  629. {opened and Close is being called}
  630. {inappropriately.}
  631.         if DCE^.dCtlWindow = nil then
  632.             Exit(Close);
  633.  
  634. {Clean up after ourselves}
  635.         with DCE^ do begin
  636.                 ModifyWindows(FrontWindow, CloseAWindow);
  637.                 dctlWindow := nil;
  638.  
  639.                 for i := 0 to WindowMenu do
  640.                     ReleaseResource(handle(OurMenus[i]));
  641.                 Disposhandle(SavedMenuList);
  642.                 dCtlMenu := 0;
  643.  
  644.             end;
  645.     end; {of function CLOSE}
  646.  
  647.  
  648. { ================ OPEN =====================}
  649. function OPEN (DCTlE: DCtlPtr;
  650.                                 IOPB: ParmBlkPtr): OsErr;
  651.     var
  652.         aWind: WindowPeek;
  653.     begin
  654.         open := NoErr;
  655.         OurName := IOPB^.ioNamePtr^;
  656.         with DCE^ do begin
  657.  
  658.                 if dctlWindow = nil then
  659.  
  660. {The window is nil, so initialize and allocate }
  661. {everything!}
  662.                     begin
  663.                         if dCtlStorage = nil then begin
  664.                                 Sysbeep(20);
  665.                                 Open := OpenErr;
  666.                             end
  667.                         else begin
  668.                                 dctlWindow := OpenAWindow;
  669.                                 dCtlMenu := InitMenus;
  670.                             end
  671.                     end
  672.  
  673.                 else begin
  674. {If we get here, then we are already open.  We }
  675. {must bring all our windows forward. First bring }
  676. {forward all windows below our topmost DA window. }
  677. {This has the effect of bringing forward ALL our }
  678. {windows when the DA is selected without changing }
  679. {their relative order. Next select our topmost }
  680. {window to bring it to the front.  The topmost }
  681. {window is already stored in the DCE^.dctlWindow }
  682. {field.}
  683.  
  684.                         aWind := WindowPeek(dctlWindow)^.nextWindow;
  685.                         ModifyWindows(aWind, BringWindowForward);
  686.  
  687.  {Now select our topmost window}
  688.                         SelectWindow(dctlWindow);
  689.                     end;
  690.             end; {of WITH clause}
  691.     end; {of OPEN}
  692.  
  693. {=================== CONTROL =================== }
  694. function CONTROL (IOPB: ParmBlkPtr): OsErr;
  695.     const
  696.         accGoodBye = -1;
  697.     type
  698.         EventPtr = ^EventRecord;
  699.     var
  700.         APort: GrafPtr;
  701.  
  702.     begin {functon CONTROL}
  703.  
  704.         Control := NoErr;
  705.         GetPort(aPort);
  706.         SetPort(DCE^.dctlWindow);
  707.  
  708. {Dispatch for all the different control calls}
  709.         case IOPB^.csCode of
  710.             accEvent: 
  711.                 HandleEvents(EventPtr(IOPB^.ioMisc)^);
  712.             accCursor: 
  713.                 DoIdle;
  714.             accMenu: 
  715.                 DoMenus(IOPB^.csParam[0], IOPB^.csParam[1]);
  716.             accGoodbye: 
  717.                 Control := Close;
  718.             otherwise
  719.         end; {of case statement}
  720.  
  721.         SetPort(APort);
  722.     end; {function CONTROL}
  723.  
  724.  
  725. {******************************************}
  726. {************MAIN FUNCTION*****************}
  727. {******************************************}
  728. {FUNCTION Main is the entry point for the DA. }
  729. {THINK Pascal will intercept the driver }
  730. {(OPEN/CLOSE/CONTROL) call and funnel it through }
  731. {this routine, along with a pointer to the device }
  732. {control entry, the parameter block, and a selector }
  733. {indicating which driver call was made. Pascal very }
  734. {nicely sets up a global storage block}
  735. function MAIN (DCtlE: DCtlPtr;
  736.                                 IOPB: ParmBlkPtr;
  737.                                 driveCall: Integer): OSErr;
  738.     begin
  739. {Need to call RememberA4 up front if our dialogs }
  740. {have user items or if we get called at interrupt }
  741. {time.  This code doesn’t use these features, but }
  742. {call it just in case.}
  743.         RememberA4;
  744.         Done := false;
  745.         DCE := DCtlE;
  746.  
  747.         case driveCall of
  748.  
  749.             DriverOpen: 
  750.                 Main := Open(DCtlE, IOPB);
  751.  
  752.             DriverControl: 
  753.                 with DCE^ do begin
  754. {Turn off further control calls while servicing }
  755. {this one in order to avoid re-entrancy issues}
  756.                         dCtlFlags := BitAnd(dCtlFlags, $FBFF);
  757.                         Main := Control(IOPB);
  758.                         dCtlFlags := BitOr(dCtlFlags, $0400);
  759.                     end;
  760.  
  761.             DriverClose: 
  762.                 if NumWindows > 1 then
  763. {We have more than one window open, so just close }
  764. {topmost}
  765.                     begin
  766.                         CloseAWindow(FrontWindow);
  767.                         Main := CloseErr
  768.                     end
  769.                 else
  770.                     Main := Close;
  771.  
  772.             DriverStatus, DriverPrime: 
  773.                 Main := NoErr;
  774.  
  775.         end; {case statement}
  776.  
  777. {This happens when the user selects “quit” from }
  778. {the menus or there is a fatal error.}
  779.         if Done then begin
  780.                 Main := Close;
  781.                 CloseDeskAcc(DCE^.dctlRefnum);
  782.             end
  783.     end; {of MAIN function}
  784.  
  785. end. {of the whole unit}